//#include <bits/stl_alloc.h>
#include <stdexcept>
#include <string>
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
#include <sstream>
#include <fstream>
#include <deque>

//	boost
#include <boost/foreach.hpp>
#include <boost/iostreams/code_converter.hpp>
#include <boost/iostreams/device/mapped_file.hpp>

//# define DEBUG_LEVEL 9
#include <debug_func.h>
#include <perlutil.h>
#include <ultoa.h>
#include <bl2seq_mine.h>
#include <portable_timer.h>
#include <progress_indicator.h>
#include <bcpp_parsefasta.h>
#include <bcpp_read_seq.h>
#include <stlhelper.h>
#include <string_hash.h>
#include <print_error.h>
#include <ext/hash_map>
#include <gc.h>
#include <open_fstream.h>
#include <open_stream_name_with_substitutions.h>

#include "get_arg.h"

using std::string;
using std::ifstream;
using std::ofstream;
namespace io = boost::iostreams;




int main(int argc, char* argv[])
{
	debug_func_cerr( 8 );

	t_program_args args;

	try
	{

		// don't use C streams
		std::ios::sync_with_stdio(false);
	
		// get command line arguments
		if (!process_command_line_options(argc, argv, args))
			return 1;

		BOOST_FOREACH(const string& input_file, args.input_files) 
		{

			args.VERBOSE() << "\tReading sequence from " << input_file << "\n";


			//	
            //  open memory mapped file
            //
			io::mapped_file_source mapfile;
			mapfile.open(input_file);
			if (!mapfile.size())
			{
				std_print_warning(args.CERR(), input_file + " is empty!\n");
				continue;
			}
			
			//// try opening : warn if fail
            //std::ifstream ifs;
			//try
			//{
			//	std::istream::pos_type fsize = open_fstream(ifs, input_file);
			//} 
			//catch ( std::exception& e )
			//{
			//	std_print_warning(args.CERR(), e.what());
			//	continue;
			//}

			//
            //  Skipping past accession line of mapped data
            //
			const char * buf = mapfile.data();
			const char * bufend = mapfile.data() + mapfile.size();
			if (*buf == '>')
			{
				++buf;
				while (buf != bufend && *buf != '\n')
					++buf;
				if (buf == bufend)
				{
					std_print_warning(args.CERR(), input_file + " only has an accession line!\n");
					continue;
				}
				++buf;
			}
			
			
			if (buf == bufend)
			{
				std_print_warning(args.CERR(), input_file + " only has an accession line!\n");
				continue;
			}

			// calculate chromosome length
			unsigned chromosome_length = mapfile.size() - ( buf - mapfile.data() );

			// 
			//  try opening result
			//
			std::ofstream ofs;
			std::string output_file_name;
			try
			{
				open_stream_with_substitutions(ofs, output_file_name,  args.results_path, input_file);
			} 
			catch ( std::exception& e )
			{
				std_print_warning(args.CERR(), "Can't open results file for " + input_file + 
								". " + e.what());
				continue;
			}
			args.VERBOSE() << "\tWriting GC to " << output_file_name << "\n";


			//
            //  analyse blocks
            //
            unsigned	from_start = 0;
			unsigned cnt_windows = chromosome_length / args.windows_size + 1;
			t_progress_indicator dots(args.VERBOSE(), cnt_windows / 100 + 1);
			dots.set_limit(cnt_windows);
			dots.use_timer();
			while (buf != bufend)
			{
				++dots;
				unsigned counts_table[256];
				std::fill(counts_table,  counts_table + 256,  0);
				const char* blockend = buf + std::min(bufend - buf, (int)args.windows_size);
				while (buf != blockend)
				{
					++counts_table[static_cast<unsigned>(*buf)];
					++buf;
				}
				
				unsigned gc_count = counts_table[static_cast<unsigned>('c')]	+
									counts_table[static_cast<unsigned>('g')]	+
									counts_table[static_cast<unsigned>('C')]	+
									counts_table[static_cast<unsigned>('G')];
				unsigned total_count = 
									counts_table[static_cast<unsigned>('a')]	+
									counts_table[static_cast<unsigned>('t')]	+
									counts_table[static_cast<unsigned>('A')]	+
									counts_table[static_cast<unsigned>('T')]	+
									gc_count;
				if (total_count)
					ofs
						<< from_start << "\t"
						<< gc_count * 1.0 / total_count << "\t"
						<< chromosome_length - from_start << "\n";
				from_start += args.windows_size;
				
					
			}

		}

	}
	catch (std::exception& e)
	{
		std_print_error(args.CERR(), e.what());
		return 1;
	}
	catch(...)
	{
		return 1;
	}

	return 0;
}
